package org.jsbd.boss.service.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;

import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.jsbd.boss.common.DataGridModel;
import org.jsbd.boss.common.redis.RedisClient;
import org.jsbd.boss.dao.IChannelApkConfigDAO;
import org.jsbd.boss.dao.IInviteCodeConfigDao;
import org.jsbd.boss.dao.IInviteCodeDao;
import org.jsbd.boss.domian.InviteCodeBean;
import org.jsbd.boss.domian.channel.ChannelApkConfig;
import org.jsbd.boss.domian.channel.InviteConfig;
import org.jsbd.boss.domian.channel.InviteConfigProbability;
import org.jsbd.boss.service.IInviteCodeService;
import org.jsbd.boss.util.CacheKey;
import org.jsbd.boss.util.NumUtil;
import org.jsbd.boss.util.RandomUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import edu.hziee.common.queue.DelayExecuteBuffer;

@Service("inviteCodeService")
public class InviteCodeServiceImpl implements IInviteCodeService {

	@Autowired
	private IInviteCodeDao inviteCodeDao;

	@Autowired
	private DelayExecuteBuffer<InviteCodeBean> inviteCodeBuffer;

	@Resource
	private CacheParamManager cacheParamManager;

	@Resource
	private RedisClient<String, Object> redisClient;
	
	@Resource
	private IInviteCodeConfigDao inviteCodeConfigDao;
	
	@Autowired
	private IChannelApkConfigDAO channelApkConfigDAO;

	@Override
	public void saveInviteCode(InviteCodeBean inviteCode) {
		Object maxTimes = cacheParamManager.getParamValue(CacheKey.INVITE_CODE_MAXNUM);
		int maxTime = 10;
		if (maxTimes != null) {
			maxTime = Integer.parseInt(maxTimes.toString());
		}
		inviteCode.setTotalCount(RandomUtils.getRandomNum(5) + maxTime);
		inviteCode.setRemainCount(inviteCode.getTotalCount());
		inviteCode.setRandom(RandomUtils.getRandomDouble());
		inviteCodeBuffer.add(inviteCode);
	}

	@Override
	public void updateInviteCode(InviteCodeBean inviteCode) {
		inviteCodeDao.updateInviteCode(inviteCode);
	}

	@Override
	public void updateInviteCode(List<InviteCodeBean> inviteCodeList) {
		for (InviteCodeBean invite : inviteCodeList) {
			invite.setRandom(RandomUtils.getRandomDouble());
			invite.setRemainCount(invite.getRemainCount() - 1);
		}
		inviteCodeDao.updateInviteCode(inviteCodeList);
	}

	@Override
	public String selectInviteCode(String mark) {
		String key = CacheKey.INVITE_CODE_QUEUE_KEY + mark;
		String code = (String) redisClient.pollFromQueue(key);
		if (StringUtils.isBlank(code)) {
			initInviteCode(mark);
		}
		return code;
	}

	/**
	 * initInviteCode:获取邀请码
	 * 
	 * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
	 * @param mark
	 */
	private void initInviteCode(final String mark) {
		new Thread() {

			@Override
			public void run() {
				String lockKey = CacheKey.INVITE_CODE_QUEUE_KEY + "_LOCK";
				Boolean lock = redisClient.evalSetNx(lockKey, 30);
				if (lock != null && lock) {
					List<InviteCodeBean> list = inviteCodeDao.getInviteCodeList(mark);
					if (list != null && list.size() > 0) {
						List<String> codeList = new ArrayList<String>();
						for (InviteCodeBean code : list) {
							codeList.add(code.getInviteCode());
						}
						redisClient.putToQueue(CacheKey.INVITE_CODE_QUEUE_KEY + mark, codeList.toArray(new String[0]));
						updateInviteCode(list);
					}
					redisClient.remove(lockKey);
				}
			};
		}.start();

	}

	@Override
	public Map<String, Object> getConfigList(DataGridModel page, InviteConfig inviteConfig) {
		return inviteCodeConfigDao.getConfigList(page, inviteConfig);
	}

	@Override
	public void add(InviteConfig inviteConfig) {
		inviteCodeConfigDao.save(inviteConfig);
		
	}

	@Override
	public InviteConfig getById(Integer id) {
		return inviteCodeConfigDao.getById(id);
	}
	
	
	@Override
	public void update(InviteConfig inviteConfig) {
		inviteCodeConfigDao.update(inviteConfig);
	}

	@Override
	public void delete(Integer id) {
		inviteCodeConfigDao.delete(id);
	}

	@Override
	public InviteConfig getByMark(String mark) {
		return inviteCodeConfigDao.getByMark(mark);
	}

	@Override
	public boolean hasMark(String mark) {
		ChannelApkConfig channelApkConfig = channelApkConfigDAO.getByMark(mark);
		InviteConfig inviteConfig  = inviteCodeConfigDao.getByMark(mark);
		if (null == channelApkConfig) {
			return false;
		}
		if (null != channelApkConfig && null != inviteConfig) {
			return false;
		}
		return true;
	}

	@Override
	public void saveInviteCodeNew(InviteCodeBean inviteCodeBean) {
		double random = RandomUtils.getRandomDouble();
		Integer totalCount = 0;
		
		String parentCode = inviteCodeBean.getParentCode();
		String mark = inviteCodeBean.getMark();
		
		float p1,p2,p3,p4;

		InviteConfig ic = inviteCodeConfigDao.getByMark(mark);
		//float rateFirst = ic.getRateFirst();
		float rateSecond = ic.getRateSecond();
		float rateThird = ic.getRateThird();
		float rateFourth = ic.getRateFourth();
		Float totalCountAvg = ic.getTotalCountAvg();
		
		if (!StringUtils.isBlank(parentCode)) {
			InviteCodeBean parentBean = inviteCodeDao.getByParentCode(parentCode);
			if (null != parentBean) {
				Integer level = parentBean.getLevel();
				Integer currentLevel = ++level;
				
				inviteCodeBean.setParentCode(parentCode);
				inviteCodeBean.setLevel(currentLevel);
				if (currentLevel == 2 ) {
					p2 = NumUtil.divide(rateThird, NumUtil.multiply(totalCountAvg, rateSecond));
					if (random <= p2) {
						totalCount = getTotalCount(ic.getId(), ic.getTotalCount());
					}
				}
//				if (currentLevel >= 3 ) {
//					p3 = NumUtil.divide(rateThird, NumUtil.multiply(totalCountAvg, rateSecond));
//					if (random <= p3) {
//						totalCount = getTotalCount(random, ic.getId(), ic.getTotalCount());
//					}
//				}
				if (currentLevel >= 3) {
					float sum = NumUtil.add(rateThird, rateFourth);
					float product = NumUtil.multiply(totalCountAvg, sum);
//					float diff = NumUtil.subtract(rateFourth, product);
//					p4 = NumUtil.divide(rateFourth, diff);
					p4 = NumUtil.divide(rateFourth, product);
					if (random <= p4) {
						totalCount = getTotalCount(ic.getId(), ic.getTotalCount());
					}
				}
			}
		} else {
			inviteCodeBean.setLevel(1);
			float sum = NumUtil.add(rateSecond, rateThird, rateFourth);
			float diff = NumUtil.subtract(1f, sum);
			float product = NumUtil.multiply(totalCountAvg, diff);
			p1 = NumUtil.divide(rateSecond, product);
			if (random <= p1) {
				totalCount = getTotalCount(ic.getId(), ic.getTotalCount());
			}
		}
		
		inviteCodeBean.setTotalCount(totalCount);
		inviteCodeBean.setRemainCount(totalCount);
		inviteCodeBean.setRandom(random);
		inviteCodeDao.saveInviteCode(inviteCodeBean);
	}
	
	private Integer getTotalCount(Integer inviteId, String totalCount) {
		double r = RandomUtils.getRandomDouble();
		
		if (!totalCount.contains("-")) {
			return Integer.valueOf(totalCount);
		} else {
			List<InviteConfigProbability> icps = inviteCodeConfigDao.selectInviteCodeProbability(inviteId);
			float temp = 0.0f;
			for (int i = 0; i < icps.size(); i++) {
				if (i == 0) {
					if (r > 0 && r <= icps.get(i).getProbability()) {
						return icps.get(i).getTotalCount();
					}
				} else {
					if (r > temp && r <= NumUtil.add(temp, icps.get(i).getProbability()) ) {
						 return icps.get(i).getTotalCount();
					}
				}
				temp = NumUtil.add(temp, icps.get(i).getProbability());
			}
			return 0;
		}
	}

	@Override
	public void saveInviteCodeProbability(List<InviteConfigProbability> list) {
		inviteCodeConfigDao.insertInviteCodeProbability(list);
	}

	@Override
	public List<InviteConfigProbability> getInviteCodeProbability(Integer id) {
		return inviteCodeConfigDao.selectInviteCodeProbability(id);
	}

	@Override
	public void deleteInviteCodeProbability(Integer id) {
		inviteCodeConfigDao.deleteInviteCodeProbability(id);
	}

	@Override
	public void updateTotalCountAvg(Integer inviteId, Float totalCountAvg) {
		inviteCodeConfigDao.updateTotalCountAvg(inviteId, totalCountAvg);
	}
}
